-
Notifications
You must be signed in to change notification settings - Fork 70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Collect IP and MAC address properly #161
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@miha-plesko this is just a preliminary review. I'll need to dig into it more deeply to understand the changes.
Fix the VCR issue in Travis as well.
@@ -11,14 +11,14 @@ def initialize(ems, options = nil) | |||
@data_index = {} | |||
@inv = Hash.new { |h, k| h[k] = [] } | |||
@org = @connection.organizations.first | |||
@network_name_mapping = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Align on =
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
end | ||
|
||
def ems_inv_to_hashes | ||
log_header = "MIQ(#{self.class.name}.#{__method__}) Collecting data for EMS name: [#{@ems.name}] id: [#{@ems.id}]" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you removing this, but not the next line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I see now that log_header
is now a function that you use in various places. Please see my comment there.
def get_networks | ||
# fetch org VDC networks | ||
@inv[:networks] = @org.networks | ||
def log_header |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move to the bottom, I guess, because it's a util function that should not interfere with the logic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
# fetch org VDC networks | ||
@inv[:networks] = @org.networks | ||
def log_header | ||
"MIQ(#{self.class.name}.#{__method__}) Collecting data for EMS name: [#{@ems.name}] id: [#{@ems.id}]" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
log_header
function must not include the part for Collecting data for ...
-- this is only useful in ems_inv_to_hashes
where you are showing the high level description. All other log messages should only contain information about the location of the message.
BTW, I haven't tested, but will __method__
return the name of the actual function or simply log_header
function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'll return "log_header"
, if you want to do this you'll need to use caller_location
like we do in vmdb_logger
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Thanks for noticing, I've added the debug logging ad hoc just prior opening the PR hence such silly mistakes. I should have examined the result better...
6cc0c08
to
ea6a558
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review, I've applied the comments and repushed. Also, I've added some non-VCR unit tests for NetworkParser, please see.
@@ -11,14 +11,14 @@ def initialize(ems, options = nil) | |||
@data_index = {} | |||
@inv = Hash.new { |h, k| h[k] = [] } | |||
@org = @connection.organizations.first | |||
@network_name_mapping = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
def get_networks | ||
# fetch org VDC networks | ||
@inv[:networks] = @org.networks | ||
def log_header |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
# fetch org VDC networks | ||
@inv[:networks] = @org.networks | ||
def log_header | ||
"MIQ(#{self.class.name}.#{__method__}) Collecting data for EMS name: [#{@ems.name}] id: [#{@ems.id}]" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Thanks for noticing, I've added the debug logging ad hoc just prior opening the PR hence such silly mistakes. I should have examined the result better...
59a9c54
to
361926a
Compare
@agrare all best in 2018! When you can, I kindly ask to push this PR forward so that we close the BZ at some point. |
Of course @miha-plesko sorry for the delay, I'll get to it soon |
@agrare I realize that this specific PR seems like a hell to review 😄 But it's actually just a single file (refresh_parser.rb) with tons of VCR tests. Knowing that the current upstream refresh parser is missing a lot of data - because vmware REST API is quite a mess and not all data was scrubbed yet - you can actually consider the whole file as newly added, instead struggling with the diff. Anyway, I'm looking forward to your review. Please let me know if I can help you in any way. I think I could even help you get access to a running vCD installation if you need? |
@Ladas would you care to skim over this PR? It's about network inventoring. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks good and there are nice specs, so it looks good to me. 👍
Is there some plan for Graph Refresh? It would cleanup the parser code a bit.
Yes, I was thinking about graph refresh quite a lot when reimplementing, will definetly do it at some point because I don't want our provider to use ancient approaches 😄 |
@inv[:networks] = @org.networks | ||
def get_vdc_networks | ||
@inv[:vdc_networks] = @org.networks | ||
@inv[:vdc_networks_idx] = Hash[@inv[:vdc_networks].map { |net| [net.id, net] }] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can just use #index_by
here, @inv[:vdc_networks_idx] = @inv[:vdc_networks].index_by(&:id)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't know about this one, thanks.
# fetch org VDC networks | ||
@inv[:networks] = @org.networks | ||
def get_vdc_networks | ||
@inv[:vdc_networks] = @org.networks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably a good idea to do @org.networks || []
in case this returns nil
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, done.
if (vdc_net = corresponding_vdc_network(net_conf, @inv[:vdc_networks_idx])) | ||
$vcloud_log.debug("#{log_header} skipping VDC network duplicate") | ||
memorize_network_name_mapping(stack.ems_ref, vdc_net.name, vdc_net.id) | ||
next |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of skipping the loop can you just move this into the else
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 shouldn't need the next now
def get_network_ports | ||
@inv[:nics] = [] | ||
@ems.vms.each do |vm| | ||
fetch_nic_configurations_for_vm(vm.ems_ref).each do |nic| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to get all NICs up front instead of one-off API calls per-VM? That way it won't slow down dramatically as the number of VMs increases (same comment for fetch_network_configurations_for_vapp
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we can avoid an N+1 call we should definetely go for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
VMware network refresh parser is so complicated because VMware API is surprisingly modest when it comes to networking. In other words, I wasn't able to find a better way to obtain networking information other than looping through VMs and querying for their networking configuration. API documentation that I was looking at is here: https://www.vmware.com/support/vcd/doc/rest-api-doc-1.5-html.
I'd feel much better if there were API calls like "list all networks", "list all subnets", "list all NICs", "list all Routers", "list all floating IPs" ... but they just aren't there. I'm not sure how anyone was ever able to use the API to reconstruct networking information for vCD to be honest 😄
Is it possible that VMware API is so modest with information, or did I miss something big?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay that is unfortunate, is this data that is usually returned with the VM but since we have to split up the parser by manager (cloud vs network) we have to get it again?
Asking because we're planning on unifying the managers into a single provider and single refresh so this issue might go away.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, networking data is generally fetched together with VM data. But on this PR we paid extra attention to only fetch networking data for each VM (without other stuff about VM that we assume is already fetched by cloud provider), that's why parsing is a bit complicated (reading from XML directly).
Are you referring to graph refresh? We plan on implementing it very soon (probably as soon as this week).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really related to graph refresh, although that sounds great. What we want is to unify all of the managers so instead of having 2 or 3 different workers and different refreshes this could all be done in one so we wouldn't have to fetch the data again just because this is in a different manager
uid = port_id(net_if) | ||
vm_uid = net_if[:vm].id | ||
def parse_network_router(router) | ||
parent_id = router[:parent_net].id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you're going to align the =
below lets do the same here 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Woooops 😄
} | ||
|
||
# assign myself to the vapp network | ||
@data_index.fetch_path(:cloud_subnets, "subnet-#{router[:network_id]}")[:network_router] = new_result |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think you want to use #store_path
here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice one, thanks!
end | ||
|
||
def to_list(list_or_hash) | ||
list_or_hash.kind_of?(Array) ? list_or_hash : [list_or_hash].compact |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be able to just use Array(abcd)
or abcd.to_a
everywhere instead of this method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately it ain't that simple because of this one example where Array()
behaves really strange :
Array({ :key => 'val'})
=> [[:key, "val"]] # we expect [{ :key => 'val'}]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙄 you're right that is weird, looks like Array.wrap
does the right thing though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right about Array.wrap
, it works like a charm. Thanks, I've repushed.
@miha-plesko couple of things overall looks really good though |
361926a
to
a703ff3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @agrare for a great review. I'm looking forward to be hearing your opinion about VMware API modesty - can I be wrong about it?
# fetch org VDC networks | ||
@inv[:networks] = @org.networks | ||
def get_vdc_networks | ||
@inv[:vdc_networks] = @org.networks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, done.
@inv[:networks] = @org.networks | ||
def get_vdc_networks | ||
@inv[:vdc_networks] = @org.networks | ||
@inv[:vdc_networks_idx] = Hash[@inv[:vdc_networks].map { |net| [net.id, net] }] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't know about this one, thanks.
if (vdc_net = corresponding_vdc_network(net_conf, @inv[:vdc_networks_idx])) | ||
$vcloud_log.debug("#{log_header} skipping VDC network duplicate") | ||
memorize_network_name_mapping(stack.ems_ref, vdc_net.name, vdc_net.id) | ||
next |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
def get_network_ports | ||
@inv[:nics] = [] | ||
@ems.vms.each do |vm| | ||
fetch_nic_configurations_for_vm(vm.ems_ref).each do |nic| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
VMware network refresh parser is so complicated because VMware API is surprisingly modest when it comes to networking. In other words, I wasn't able to find a better way to obtain networking information other than looping through VMs and querying for their networking configuration. API documentation that I was looking at is here: https://www.vmware.com/support/vcd/doc/rest-api-doc-1.5-html.
I'd feel much better if there were API calls like "list all networks", "list all subnets", "list all NICs", "list all Routers", "list all floating IPs" ... but they just aren't there. I'm not sure how anyone was ever able to use the API to reconstruct networking information for vCD to be honest 😄
Is it possible that VMware API is so modest with information, or did I miss something big?
uid = port_id(net_if) | ||
vm_uid = net_if[:vm].id | ||
def parse_network_router(router) | ||
parent_id = router[:parent_net].id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Woooops 😄
} | ||
|
||
# assign myself to the vapp network | ||
@data_index.fetch_path(:cloud_subnets, "subnet-#{router[:network_id]}")[:network_router] = new_result |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice one, thanks!
end | ||
|
||
def to_list(list_or_hash) | ||
list_or_hash.kind_of?(Array) ? list_or_hash : [list_or_hash].compact |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately it ain't that simple because of this one example where Array()
behaves really strange :
Array({ :key => 'val'})
=> [[:key, "val"]] # we expect [{ :key => 'val'}]
a703ff3
to
19012e8
Compare
With this commit we fix a bug that prevented IP address and MAC from being collected properly. NetworkManager's refresh parser has been updated to be more robust and effective and some complex VCR unit tests were added where we ensure that things will work on a more advanced networking setup as well. Performance optimization was achieved like this: BEFORE: first all vdcs were fetched, then all vapps per vdc, then all vms per vapp. Then finally the network data was fetched from the VM, where only the most basic data was present (e.g. MAC was missing). NOW: we load VMs from VMDB (inventoried by cloud provider) and then invoke a single API call per VM to fetch networking data for it. Signed-off-by: Miha Pleško <miha.plesko@xlab.si>
19012e8
to
e5d4dce
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 LGTM, thanks @miha-plesko !
Some comments on commit miha-plesko@e5d4dce spec/models/manageiq/providers/vmware/network_manager/refresh_parser_spec.rb
|
Checked commit miha-plesko@e5d4dce with ruby 2.3.3, rubocop 0.52.0, haml-lint 0.20.0, and yamllint 1.10.0 **
app/models/manageiq/providers/vmware/network_manager/refresh_parser.rb
|
Collect IP and MAC address properly
Collect IP and MAC address properly (cherry picked from commit 04b4f8d) https://bugzilla.redhat.com/show_bug.cgi?id=1552673
Gaprindashvili backport details:
|
Collect IP and MAC address properly (cherry picked from commit 04b4f8d) https://bugzilla.redhat.com/show_bug.cgi?id=1552675
Looks I accidentally had the backport pushed to Fine branch a few days ago... reverted now. |
…g_of_import_error Fix message when import fails
With this commit we fix a bug that prevented IP address and MAC from being collected properly. NetworkManager's refresh parser has been updated to be more robust and effective and some complex VCR unit tests were added where we ensure that things will work on a more advanced networking setup as well. Performance optimization was achieved like this:
BEFORE: first all vdcs were fetched, then all vapps per vdc, then all vms per vapp. Then finally the network data was fetched from the VM, where only the most basic data was present (e.g. MAC was missing).
NOW: we load VMs from VMDB (inventoried by cloud provider) and then invoke a single API call per VM to fetch networking data for it.
Unfortunately the Fog support for vCD is far from perfect therefore the implementation might seem somewhat hacky, but as far as I know there is no better way...
BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1520372
@miq-bot assign @agrare
@miq-bot add_label bug, enhancement, gaprindashvili/yes, fine/yes
And some screenshots: